<?php
namespace command\factory;

use command\factory\api\YapiService;
use command\lib\DocParse;
use ReflectionClass;

class Controller
{
    //接口配置
    private $config = [];

    /**
     * 构造函数，初始化配置
     *
     * @param array $config
     */
    public function __construct($config=[])
    {
        $this->config = array_merge($this->config,$config);
    }

    /**
     * 初始化模型类
     *
     * @param array $config
     * @return object
     */
    public static function init(array $config)
    {
        $class = new static($config);

        return $class;
    }
    
    /**
     * 创建接口
     *
     * @return void
     */
    public function create()
    {
        
        foreach ($this->parseConfig() as $module => $config) {
            $this->createController($module,$config['controller']);
            $this->createRoute($module);
            $this->createApi($module,$config['api']);

        }

        return true;
        
    }

    /**
     * 解析配置
     *
     * @return array
     */
    private function parseConfig()
    {
        return $this->config;
    }

    /**
     * 创建控制器
     *
     * @param string $module
     * @param array $controllerList
     * @return void
     */
    private function createController($module,$controllerList)
    {

        if(empty($controllerList)){
            return true;
        }

        $modulePath = $module?$module."/":'';

        $path = app()->getBasePath().$modulePath."controller";

        if (!is_dir($path)) {
            mkdir($path, 0755, true);
        }

        $stub = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'controller/stubs' . DIRECTORY_SEPARATOR . 'controller.stub');

       
        foreach ($controllerList as $controllerName => $config) {

            $namespace = 'app\controller';

            if($module)$namespace = 'app\\'.$module.'\controller';

            $controllerName = ucfirst($controllerName);

            $pathName = $path . DIRECTORY_SEPARATOR . $controllerName .".php";

            if(is_file($pathName))continue;//文件已存在跳过

            $method = $this->buildMethod($config['methods'],$config['comment']);

            file_put_contents(
                $pathName,
                str_replace(
                    ['{$namespace}','{$use}','{$comment}','{$controllerName}','{$controllerMethod}','{$exceptLogin}','{$apiCat}'],
                    [$namespace,$method['use_str'],$config['comment'],$controllerName,$method['method_str'],$config['except_login']??'',$config['api_cat']??''],
                    $stub
                )
            );

        }

    	return true;
    } 

    /**
     * 构建逻辑处理方法
     *
     * @param array $modelConfig
     * @param string $modelName
     * @return void
     */
    private function buildMethod($methodList,$classComment)
    {
        $use = [];
        $functions = [];

        $publicTpl = $this->getMethodTpl('public');

        foreach ($methodList as $methodName=>$config) {
            $use[]='use '.($config['call_namespace']??'app\\logic').'\\'.$config['call_logic'].';';

            $validate = $this->buildValidate($config['param']);;
            if($validate){
                $validateStr = $this->getMethodTpl('validate');
                $validate = str_replace('{$validate}',$validate,$validateStr);
            }

            $methodStr = $this->getMethodTpl($config['call_type']);

            $callMethod = str_replace(['{$callMethod}','{$pk}','{$with}','{$order}'],[$config['call_method'],$config['call_pk'],$config['with']??'""',$config['order']??'""'],$methodStr);
            $functions[]=str_replace(
                ['{$comment}','{$route}','{$reqMethod}','{$param}','{$apiCat}','{$methodName}','{$validate}','{$callLogic}','{$callMethod}'],
                [$config['comment'],$config['route'],$config['req_method'],implode(',',$config['param']),$config['api_cat']??$classComment,$config['method_name'],$validate,$config['call_logic'],$callMethod],
                $publicTpl
            );
            
        }

        $res['method_str'] = implode(PHP_EOL.PHP_EOL,$functions);
        $res['use_str'] = implode(PHP_EOL,array_unique($use));

        return $res;

    }

    /**
     * 获取代码块模板
     *
     * @param string $name
     * @return string
     */
    private function getMethodTpl($name)
    {
        if(empty($this->tplStr[$name])){
            $tplFile = __DIR__ . DIRECTORY_SEPARATOR . 'controller/stubs' . DIRECTORY_SEPARATOR . 'method' . DIRECTORY_SEPARATOR . $name . '.stub';
            if(is_file($tplFile)){
                $this->tplStr[$name]=file_get_contents($tplFile);
            }else{
                return false;
            }
        }

        return $this->tplStr[$name];
    }

    /**
     * 通过参数构建必填验证
     *
     * @param string $paramstr 参数字符串
     * @return string
     */
    private function buildValidate($paramArr)
    {
        $validate = [];

        foreach ($paramArr as $value) {
            $set = explode('|',$value);
            if(!empty($set[1])&&$set[1]==1){
                $validate[] = '"'.$set[0].'|'.$set[2].'"=>"require"';
            }
        }

        if(empty($validate))return '';

        return '['.implode(',',$validate).']';
    }

    /**
     * 创建路由
     *
     * @param string $module
     * @return void
     */
    private function createRoute($module)
    {
        $modulePath = $module?$module."/":'';

        $path = app()->getBasePath().$modulePath."controller";

        if (!is_dir($path)) {
            return true;
        }
        
        $list = scandir($path);//扫描文件夹，获取文件列表

        $namespace = 'app\controller';

        if($module)$namespace = 'app\\'.$module.'\controller';

        $routeStr = '';

        $controller = config('ykcommand.auto.'.($module?$module.'.controller':'controller'));//如果指定控制器自动生成

        foreach ($list as $key => $value) {
            
            if(is_file($path.'/'.$value)){
                $arr = explode(".",$value);
                if(!empty($controller)&&!in_array($arr[0],$controller))continue;
                
                $class = new ReflectionClass($namespace.'\\'.$arr[0]);//反射类，获取类的对象

                $methods = $class->getMethods();//获取类方法列表
                $tempStr = '';
                foreach ($methods as $method) {
                    if($class->getName()!=$method->getDeclaringClass()->getName())continue;//如果是继承的方法跳过
                    $doc = $method->getDocComment();//获取方法注释
                    $methodDoc = new DocParse($doc,$class->getShortName().'/'.$method->getName());//解析方法注释
                    $tempStr .=$methodDoc->getRoute();//获得解析后的路由字符串
                }

                if($tempStr){
                    $classDoc = new DocParse($class->getDocComment()??'');//获取并解析类注释
                    $routeStr .= "/**".($classDoc->getComment()?:$class->getShortName())."*/".PHP_EOL.$tempStr.PHP_EOL;//拼接路由字符串
                }
                
            }

        }

        $stub = file_get_contents(__DIR__ .  DIRECTORY_SEPARATOR . 'route/route.stub');//获取路由文件模板

        $pathName = app()->getBasePath().$modulePath.'route/route.php';//获取写入路由文件路径和名称

        file_put_contents($pathName,str_replace(['{$route}'],[$routeStr],$stub));//写入路由文件
    }

    /**
     * 创建api文档
     *
     * @param string $module
     * @param array $apiArr
     * @return void
     */
    private function createApi($module,$apiArr)
    {

        $yapi = new YapiService($module);

        foreach ($apiArr as $key => $api) {

            $reqarr = explode(',',$api['req']);
            $req=[];
            foreach ($reqarr as $value) {
                $tempArr = explode('|',$value);
                $value = [
                    'name'=>$tempArr[0],
                    'required'=>$tempArr[1]??1,
                    'desc'=>$tempArr[2]??'',
                    'type'=>$tempArr[3]??'text'
                ];
                $req[]=$value;
            }

            $api['req']=$req;

            $yapi->syncApi($api);

        }

    	// 指令输出
    	return true;
    }
}